local skynet = require "skynet"
local PlayerService = require "modules.player.PlayerService"
local RoomConst = require "modules.room.RoomConst"
local RoomProto = require "modules.room.RoomProto"
local Msger = require "core.Msger"
local Services = require "global.Services"
local RoomEvent = require "modules.room.RoomEvent"
local Eventer = require "core.Eventer"

local table = table

---@class RoomData
local room = {
    gameName = nil,
    rule = {},
    id = nil,
    ---@type List
    playerIds = {},
    -- seat -> playerInfo
    players = {},
    -- playerId -> playerInfo
    playersById = {},
    owner = nil
}

---@class Room
local M = {
    room = room
}

--- 游戏实例
local game

--- 游戏配置
local gameConfig

function M.getData()
    return {
        id = room.id,
        gameName = room.gameName,
        players = room.players,
        owner = room.owner
    }
end

Msger.broadRoom = function(protoId, data)
    Msger.broadcast(room.playerIds, protoId, data)
end

Msger.broadWithoutId = function(playerId, protoId, data)
    Msger.broadcast(table.diff(room.playerIds, {playerId}), protoId, data)
end

local function isFull()
    return #room.playerIds >= gameConfig.maxPlayerCount
end

local function getEmptySeat()
    if isFull() then
        return false
    end

    for i = 1, gameConfig.maxPlayerCount do
        if not room.playerIds[i] then
            return i
        end
    end
end

local function playerEnter(seat, playerId)
    if room.playersById[playerId] then
        return RoomConst.ENTER_RESULT_CODE.ALREADY_IN_ROOM
    end

    if game.playerCanEnter then
        if not game.canPlayerEnter(seat, playerId) then
            return RoomConst.ENTER_RESULT_CODE.NOT_ALLOWED
        end
    end

    table.insert(room.playerIds, playerId)
    local player = {
        playerInfo = PlayerService:getPlayerInfo(playerId),
        seat = seat
    }

    room.players[seat] = player
    room.playersById[playerId] = player

    if game.onPlayerEnter then
        game.onPlayerEnter(seat, playerId)
    end

    Eventer.dispatchSync(RoomEvent.ROOM_PLAYER_ENTER, playerId, room.id)
    return RoomConst.ENTER_RESULT_CODE.SUCCESS
end

function M.enter(playerId)
    --- 进入房间
    local seat = getEmptySeat()
    if not seat then
        return RoomConst.ENTER_RESULT_CODE.ROOM_FULL
    end

    local code = playerEnter(seat, playerId)
    if code ~= RoomConst.ENTER_RESULT_CODE.SUCCESS then
        return code
    end

    return RoomConst.ENTER_RESULT_CODE.SUCCESS, M.getData()
end

local function playerExit(playerId)
    local player = room.playersById[playerId]
    if not player then
        -- 玩家不在房间中
        return RoomConst.EXIT_RESULT_CODE.NOT_IN_ROOM
    end

    local seat = player.seat
    if game.canPlayerExit then
        if not game.canPlayerExit(seat) then
            return RoomConst.EXIT_RESULT_CODE.NOT_ALLOWED
        end
    end

    table.remove(room.playerIds, table.index(room.playerIds, playerId))
    room.players[seat] = nil
    room.playersById[playerId] = nil

    if game.onPlayerExit then
        game.onPlayerExit(seat, playerId)
    end

    Eventer.dispatchSync(RoomEvent.ROOM_PLAYER_EXIT, playerId, room.id)
    return RoomConst.EXIT_RESULT_CODE.SUCCESS
end

function M.exit(playerId)
    local player = room.playersById[playerId]
    if not player then
        -- 玩家不在房间中
        return RoomConst.EXIT_RESULT_CODE.NOT_IN_ROOM
    end

    return playerExit(idx)
end

function M.dismiss(playerId)
    if room.owner ~= playerId then
        return RoomConst.DISMISS_RESULT_CODE.NOT_OWNER
    end

    Msger.broadRoom(RoomProto.ROOM_DISMISS)
    skynet.exit()
end

local function start(roomId, data, owner)
    room.id = roomId
    room.gameName = data.gameName
    gameConfig = require"modules.games.GamesConfig".games[room.gameName]
    room.rule = data.rule
    table.insert(room.playerIds, owner)
    local player = {
        playerInfo = PlayerService:getPlayerInfo(owner),
        seat = 1
    }

    table.insert(room.players, player)
    room.playersById[owner] = player
    room.owner = owner
    game = require(string.format("modules.%s.%s", room.gameName, room.gameName:upper()))
    game.init(M)
    for k, v in pairs(game) do
        M[k] = v
    end

    return true
end

function M.start(roomId, data, owner)
    local ok, err = pcall(start, roomId, data, owner)
    if not ok then
        print("err", err)
        skynet.exit()
    end

    return err
end

function M.init()
    skynet.dispatch_lua_queue(M)
end

return M
